/**
 * 
 */
package register;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.ListIterator;

import bplusfile.ByteBitSet;
import bplustree.BPlusTree;

/**
 * @author ernesto
 *
 */
public class Register {

	private LinkedList<Integer> atributes;
	private RegisterID ID;
	
	
	
	/**
	 * @return the iD
	 */
	public RegisterID getID() {
		return ID;
	}

	/**
	 * @param id the iD to set
	 */
	public void setID(RegisterID id) {
		ID = id;
	}

	/**
	 * 
	 */
	public Register() {
		atributes=new LinkedList<Integer>();
		ID=new RegisterID();
	}
	
	public Register(RegisterID id){
		atributes=new LinkedList<Integer>();
		ID=id;
	}
	
	public Register(RegisterID id,int i){
		atributes=new LinkedList<Integer>();
		ID=id;
		this.addAtribute(i);
	}
	
	/**
	 * Constructor para persistencia, se le carga el register directamente como viene del 
	 * TODO DEBERIA ARROJAR ALGUNA EXCEPCION SI EL ARCHIVO ESTA MAL
	 * @param str
	 */
	public void load(String str){
		String[] aux=str.split(getSeparatorID());
		ID.setID(aux[0]);
		if(aux.length==2){ //esto pasa solo en las hojas
			String[] aux2=aux[1].split(getSeparatorAttribute());
			for(int i=0; i<aux2.length; i++){
				atributes.addLast(Integer.valueOf(aux2[i]));
			}
		}
		//System.out.println("registro "+ ID.toString() + " leido");
	}
	/**
	 * ESPERA RECIBIR EL ARCHIVO AL COMIENZO DEL CAMPO y LO DEVUELVE EN EL BYTE SIGUIENTE AL ULTIMO
	 * @param file
	 */
	public void loadGamma(BPlusTree file){
		//TODO OOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
	}
	
	public void merge(Register reg){
		Iterator<Integer> iterator = reg.atributes.iterator();
		
		while(iterator.hasNext()){ //addAtribute ya lo ordena
			this.addAtribute(iterator.next());
		}
		
	}
	
	public void addAtribute(int i) {
		if(atributes.isEmpty()) atributes.add(i);
		int j=-1;
		int actual;
		ListIterator<Integer> iter= atributes.listIterator();
		while(iter.hasNext()) {
			j++;
			actual=iter.next();
			if(actual==i) return;
			if(actual>i){
				iter.previous(); //para compensar por el que me pase
				iter.add(i);
				return;
			}
		}
	}

	public int getSize(){
		if(atributes.isEmpty()) return 0;
		return write().length();
	}

	public String toStringList() {
		String output="";
		output=output+getSize()+"; "+ID.getID()+"||";
				
		Iterator iter= atributes.iterator();
		
		while(iter.hasNext()) output=output+iter.next().toString()+"|";  // sumo 1 para el final de campo
		return output;
	}
	
	/**
	 * Persistence string
	 */
	
	public String write() {
		String output="";
		output=ID.getID()+getSeparatorID();
				
		Iterator<Integer> iter= atributes.iterator();
		
		while(iter.hasNext()) output=output+iter.next().toString()+getSeparatorAttribute();
		return output;
	}
	
	/**
	 * Persistence string gamma coded
	 * second field is number of atributes, must replace final separator because of coding
	 * @return
	 */
	public String writeGamma(){
		String output="";
		output=ID.getID()+getSeparatorID();
		
		output=output + atributes.size() + getSeparatorID(); 
		
		LinkedList<Integer> atributesDif = convertToDiferences(atributes);
		
		Iterator<Integer> iter= atributesDif.iterator();
		ByteBitSet bset= new ByteBitSet((byte)0);
		
		int present,i,j,contador=7;
		
		while(iter.hasNext()){
			
			present=iter.next();
			for(i=0,j=1;j<=present;j*=2,i++);//i va a ser el numero de ceros y 2*i+1 el total de bits
			present-=j; //esto es lo que queda por representar en binario
			
			//Inserto los ceros correspondientes
			for(int k=i;k>0;k--){
				bset.clear(contador);
				contador--;
				if(contador==-1){//si ya hizo un byte, lo escribe
					contador=7;
					output=output+bset.toString();
					bset.clear();
				}
			}
			//Ahora inserto el 1 correspondiente al gamma
			bset.set(contador);
			contador--;
			if(contador==-1){//si ya hizo un byte, lo escribe
				contador=7;
				output=output+bset.toString();
				bset.clear();
			}
//			ahora corresponde el binario
			for(j/=2;j>=1;j/=2){
				if(present-j>=0){ //si entra esa potencia es que corresponde un 1 y restarla
					present-=j;
					bset.set(contador);
					contador--;
					if(contador==-1){//si ya hizo un byte, lo escribe
						contador=7;
						output=output+bset.toString();
						bset.clear();
					}
				} else{ // si no entra la potencia, corresponde un 0
					bset.clear(contador);
					contador--;
					if(contador==-1){//si ya hizo un byte, lo escribe
						contador=7;
						output=output+bset.toString();
						bset.clear();
					}
				}
			}
						
		}
		// ahora tengo que completar el ultimo byte para que escriba bien. No importa como termina
		output=output+bset.toString();
		return output;
	}

	private LinkedList<Integer> convertToDiferences(LinkedList<Integer> atributes2) {
		LinkedList<Integer> list=new LinkedList<Integer>();
		if(atributes2.isEmpty()) return list;
		ListIterator<Integer> iter2=atributes2.listIterator();
		//si no tenia nada ya salio, o sea que al menos uno hay
		int prevAbs,currAbs;
		prevAbs=0;
		while(iter2.hasNext()){
			currAbs=iter2.next();
			list.addLast(currAbs-prevAbs);
			prevAbs=currAbs;
		}
		return list;
		
	}
	
	private LinkedList<Integer> convertToAbsolutes(LinkedList<Integer> atributes2) {
		LinkedList<Integer> list=new LinkedList<Integer>();
		if(atributes2.isEmpty()) return list;
		ListIterator<Integer> iter2=atributes2.listIterator();
		//si no tenia nada ya salio, o sea que al menos uno hay
		int prev;
		prev=0;
		while(iter2.hasNext()){
			prev+=iter2.next();
			list.addLast(prev);
		}
		return list;
		
	}

	private String getSeparatorAttribute() {
		return "_";
	}

	private String getSeparatorID() {
		return "#";
	}

}
